//Terrain.h
//Created 07/02/14
//Created By Daniel Bowler
//
//Manages the entire (outdoor) terrain system for this engine. I have decided
//to have a patch system - a terrain is made up of several patches. This, therefore
//works best with Quantum Theory terrain stuff... In addition, we can then implement
//a nice optimisation where we can cull patches easily before rendering them. 
//
//NOTE: Whilst patches do support RAW files - I suggest not using them. Also note, 
//for this system to work best, you should have a lot of small (ish - I have
//had success with 128*128) tiles. Eg 16 128*128 tiles is better than 1 512*512 tile. 
//
//A useful tool to split up large images: imagesplitter.net/

#pragma once

//Inherits from Advanced model. The general idea is that
//the end user just adds it to the batch and it automatically works :)
#include "AbstractAdvancedModel.h"

//Include terrain patch. We will be creating a number of these
#include "TerrainPatch.h"

//Decide which terrain to use - Uncomment one.
//
//Following are 1024*1024 OR 2048*2048:
//
//#define TERRAIN_USE_TYROL
#define TERRAIN_USE_SMOOTH_HILLS
//
//Follwoing are 2048*2048 terrains (only atm):
//
//#define TERRAIN_USE_HOLE
//#define TERRAIN_USE_ALPINE
//#define TERRAIN_USE_JAGGED
//#define TERRAIN_USE_SMOOTH_HILLS_2048 //2048*2048 version.

//If uncommented, output to the debug logger how many patches we are drawing (per frame)
//#define DEBUG_OUTPUT_TERRAIN_FRUSTUM_CULL_STATS
//If uncommented, output to the debug logger the min/max X/Z locations in world
//space of the terrain - Output after loading the heightmap.
//
//MinX|MaxX|MinZ|MaxZ
//#define DEBUG_OUTPUT_TERRAIN_MIN_MAX_DIMENTIONS

//Size of the main terrain height map - length and depth must be the same (256*256, not
//256*128...)
#define TERRAIN_SIZE 1024.0f //Lenght and width must match - Square terrains only!
#define TARGET_PATCH_SIZE 64.0f
//How much to scale the heights down in the heightmap
#define TERRAIN_SCALE_FACTOR 100.0f
//Patch cell size for main terrain - 1.0f == 1 vertex per 1 world space unit - good default
#define TERRAIN_CELL_SIZE 2.0f
//Translation in the Y direction - applies to all patches. (This modifies sea-level)
#define TERRAIN_TRANSLATION_Y -75.0f

//Num patches X and Z Direction
#define TERRAIN_PATCHES_COUNT_X TERRAIN_SIZE / TARGET_PATCH_SIZE
#define TERRAIN_PATCHES_COUNT_Z TERRAIN_SIZE / TARGET_PATCH_SIZE

//Water height - also the height of the clip plane for reflection. 
#define WATER_HEIGHT 0.0f

//Forward declare skydome
class SkyDome;

//Forward declare static model - we can load
//some static terrains (QT) if we want
class StaticModel;

//Forward declare the terrain water class
class TerrainWater;

//Structure representing a single patch
struct TerrainPatchData
{
	TerrainPatchData()
	{
		//Init pointer to NULL. 
		Patch = 0;
		//Default to true - set this to false in LoadSurroudningTerrainPatches().
		IsMainTerrainPatch = true;

		PatchIdX = -1;
		PatchIdZ = -1;
	}
	~TerrainPatchData()
	{
		SafeDelete(Patch);
	}

	//The patch itself
	TerrainPatch* Patch;
	//AABB of the patch
	XNA::AxisAlignedBox PatchAABBL;
	
	//Flag indicating if this patch is part of the main terrain. False suggests
	//it is a surounding piece
	bool IsMainTerrainPatch;
	//If this patch is a main patch, it will have a 2D coordinate (or patchID)
	int PatchIdX;
	int PatchIdZ;
};

class Terrain : public AbstractAdvancedModel
{
public:
	//Standard constructor and destructor
	Terrain();
	~Terrain();

	//Inits the terrain. 
	void Init(ID3D11Device* device, SkyDome* skydome);

	//Update the terrain per frame - this animates the water normal maps
	void Update(float delta);

	//Returns the height (Y coord) at a given X,Z coordinate. This allows you
	//to attack the camera to the terrain or place objects on the terrain without
	//having to guess (Goes without saying - but if you want to do this, make
	//sure to create the terrain first). 
	//
	//Returns -1.0f if x,z is out of the bounds of the main terrain. 
	float GetHeight(float x, float z);

	//Returns sea level. 
	float GetSeaLevel(){return TERRAIN_TRANSLATION_Y;};

	//Debug Render water reflection & refaction map - call after GraphicsInterface::Render()
	void DebugDrawRefactionTexture(ID3D11DeviceContext* context);
	void DebugDrawReflectionTexture(ID3D11DeviceContext* context);

private:
	//Called by Graphics Interface - this then calls the render functions
	//for each layer. 
	void Render(bool isZPrePass, bool isOpaquePass, bool isShadowPass,		   //Flags telling us what pass it is. 
		VirtualCamera* playerCamera,										   //Player camera - passed from GraphicsInterface
		DirecLightShadowMapData* shadowMap,									   //Single shadow map - passed from GraphicsInterface. 
		SSAOData* ssaoMap,													   //SSAO Map.
		ID3D11ShaderResourceView* envMap,									   //Environment map. 
		ID3D11ShaderResourceView* llb,                                         //Light list buffer - gloabl set of lights. 
		ID3D11ShaderResourceView* llibO, ID3D11ShaderResourceView* llsebO,     //Opaque Tile Based Forward buffers. 
		ID3D11ShaderResourceView* llibT, ID3D11ShaderResourceView* llsebT,     //Transparant pass. 
		DirectionalLight* dLightArr, unsigned dLightCount,					   //Directional lights and light count. 
		XMFLOAT3 &hemiColDown, XMFLOAT3 &hemiColRange,						   //Hemispherical ambient lighting data.
		CXMMATRIX &view, CXMMATRIX &proj,									   //Cameras view/proj or the shadow maps view/proj depending on the pass. 
		ID3D11DeviceContext* context);										   //Context we are rendeirng through. 

	//Called when the terrain is added to the batch - here, we could
	//add models to the batch that this class is in charge of (eg, if
	//we use StaticModels for our surounding terrain)
	void OnBatchAdd(GraphicsInterface* sharedGraphicsInterface);

private:
	//Vector of patches
	std::vector<TerrainPatchData*>terrainPatches;
	//Vector of patches that pass the frustum test - When its the Z-PrePass
	//stage, we do frustum culling. Saving the results for when we need to 
	//do colour writes in this vector.
	std::vector<TerrainPatchData*>terrainPatchesToRender;

	//Static models - EG: QTs terrains. 
	std::vector<StaticModel*>terrainStaticModels;

	//Terrain heights
	float terrainHeights[(UINT)TERRAIN_SIZE][(UINT)TERRAIN_SIZE];

	//Vector of terrain tex data structs
	std::vector<TerrainPatchTextureData*>textureData;

	//Main terrain size - used to detect if we are inside the 
	//main terrain area before sampling the heights array. 
	float mainTerrainSizeMaxX;
	float mainTerrainSizeMaxZ;
	float mainTerrainSizeMinX;
	float mainTerrainSizeMinZ;

private:
	//Water layer. 
	TerrainWater* terrainWater;

	//Skydome
	SkyDome* skyDome;

private:
	//Since our system splits up a large heightmap in to smaller heightmaps, we
	//develop an issue where the edges of the quads do not meet up perfectly leaving
	//very obviousl holes in the terrain. We therefore need to precompute the
	//edge heights which the two quads will share. 
	//
	//Scales the heights down by heightDivScale. 
	void PreComputeHeights(const char* heightMapFull, float heightDivScale);
	
	//Calculate the height values for all of the patches edges. 
	void CalculateEdgeHeights(UINT numPatchesX, UINT numPatchesZ, 
		UINT patchVertexX, UINT patchVertexZ,
		UINT patchSizeX, UINT patchSizeZ);

	//Extracts the heights for a patch from the input vector
	void ExtractHeights(std::vector<float>&out, 
		unsigned patchI, unsigned patchJ, unsigned patchWidth, unsigned patchDepth);

	//Extracts heights for a patch  -used for medium quality patch
	void ExtractMedHeights(std::vector<float>&out, 
		unsigned patchI, unsigned patchJ, unsigned patchWidth, unsigned patchDepth);

private:
	//Loads all textures
	void LoadTerrainTexData(ID3D11Device* device);

	//Loads all of our terrain patches for the main area.
	void LoadTerrainPatches(ID3D11Device* device);

	//Optionally load additional terrain patches that make up the surounding area
	//of the level. These probably are loaded using the standard (non advanced)
	//TerrainPatch::Init() function. 
	void LoadSurroundingTerrainPatches(ID3D11Device* device);

	//Optionally load some static models for the scene - this can be the QT
	//terrains that I paid good money for! Note that these models are not 
	//part of the QuadTree like structure... Just add them to the GraphicsInterface
	//batch every frame and let our renderer do its thing. 
	void LoadStaticModelTerrainObjects(ID3D11Device* device);

	//Creates the water vb and the required textures
	void CreateWater(ID3D11Device* device);

private:
	//Calculates the main (walkable) terrain dementions in world space. 
	void CalculateMainTerrainSize();

	//Returns the terrain width and depth
	float GetDepth();
	float GetWidth();

private:
	//Loads individual terrain patch
	void LoadPatch(ID3D11Device* device, 
		const char* heightMapFile, 
		bool isPNG,
		float terrainCellSize, 
		float heightDivFactor, 
		XMFLOAT3 patchLocation, 
		float patchRotYRad, 
		TerrainPatchTextureData* patchTexData,
		bool isSlopeBased, 
		float height1 = 0.0f, float height2 = 0.0f);

	//Uses the advanced TerrainPatch::InitAdvanced() method of creating 
	//our patch
	void LoadPatchAdvanced(ID3D11Device* device, 
		std::vector<float>&heights, 
		std::vector<float>&medHeights,
		float width, float depth, 
		float terrainCellSize,  
		XMFLOAT3 patchLocation, 
		float patchRotYRad, 
		TerrainPatchTextureData* patchTexData,
		UINT patchIDX, UINT patchIDZ,
		bool isSlopeBased, 
		float height1 = 0.0f, float height2 = 0.0f);

	//Preforms frustum culling on all patches. 
	void PreformFrustumCull(VirtualCamera* cam);

	//Preforms frustum test on a patch.
	//Returns: 1 == Partial intersection, 2 == box is fully inside the frustum. 0 == outside.
	int PreformFrustumTestOnPatch(TerrainPatchData* patch, VirtualCamera* cam);
};	